﻿using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using Commons.Collections;
using Kent.Boogaart.HelperTrinity;
using Kent.Boogaart.HelperTrinity.Extensions;
using NVelocity;
using NVelocity.App;
using NVelocity.Runtime;

namespace Kent.Boogaart.Gauntlet.OutputGenerators
{
	/// <summary>
	/// An implementation of <see cref="IOutputGenerator"/> that uses an NVelocity template to generate output.
	/// </summary>
	/// <remarks>
	/// <para>
	/// This output generator assumes that the template in the context is an NVelocity template. It places each item in the context's
	/// state bag into the NVelocity context. In addition, it adds the context itself with a key of "Context". This allows the template
	/// to access items in the context using syntax such as <c>$Context.Timestamp</c>.
	/// </para>
	/// </remarks>
	public class NVelocityOutputGenerator : IOutputGenerator
	{
		private const string _contextKey = "Context";

		/// <summary>
		/// Generates the output by applying the template (which must be an NVelocity template) to the items in the context's state bag.
		/// </summary>
		/// <param name="context">
		/// The context.
		/// </param>
		public void Generate(Context context)
		{
			context.AssertNotNull("context");
			ExceptionHelper.ThrowIf(context.State.ContainsKey(_contextKey), "StateContainsContextVariable");
			
			VelocityEngine velocityEngine = new VelocityEngine();
			velocityEngine.Init(GetProperties(context.TemplateFile));
			Template template = velocityEngine.GetTemplate(context.TemplateFile.Name);
			VelocityContext velocityContext = new VelocityContext();

			//context is always available via the $Context variable
			velocityContext.Put(_contextKey, context);

			//copy the state of the context into the velocity context so the velocity template can access it
			foreach (KeyValuePair<string, object> keyValuePair in context.State)
			{
				velocityContext.Put(keyValuePair.Key, keyValuePair.Value);
			}

			using (StreamWriter streamWriter = new StreamWriter(File.Create(context.OutputFile.FullName)))
			{
				//generate the output
				template.Merge(velocityContext, streamWriter);
			}
		}

		private static ExtendedProperties GetProperties(FileSystemInfo templateFile)
		{
			Debug.Assert(templateFile != null);
			ExtendedProperties properties = new ExtendedProperties();
			properties.AddProperty(RuntimeConstants.RESOURCE_LOADER, "file");
			properties.AddProperty("file.resource.loader.description", "NVelocity File Resource Loader");
			properties.AddProperty("file.resource.loader.class", "NVelocity.Runtime.Resource.Loader.FileResourceLoader,NVelocity");
			properties.AddProperty("file.resource.loader.path", Path.GetDirectoryName(templateFile.FullName));
			properties.AddProperty("file.resource.loader.cache", "false");

			return properties;
		}
	}
}